#include "Resource.h"
#include <iostream>

/**
 * Constructor.
 */
Resource::Resource(int id, pid_t firstOwner, LockType firstLock) : id_(id)
{
	currentOwners_.insert(std::pair<int, LockType>(firstOwner, firstLock));
}

/**
 * Method used to remove owner with specified PID.
 */
bool Resource::removeOwner(pid_t pid)
{
	return (currentOwners_.erase(pid) == 1);		
}

/**
 * Method used to add owner with a specified PID and LockType.
 */
bool Resource::addOwner(pid_t pid, LockType type)
{
	/** Jeśli już ma dostęp to z niego rezygnuje i ubiega się o inny */
	std::map<pid_t, LockType>::iterator iter;
	if((iter = currentOwners_.find(pid)) != currentOwners_.end())
		currentOwners_.erase(pid);

	if(!canAddOwner(pid, type))
	{
		waitingProcesses_.insert(std::pair<int, LockType>(pid, type));
		return false;
	}
		
	currentOwners_.insert(std::pair<int, LockType>(pid, type));
	
	return true;
}

/**
 * Method used to check whether adding owner with a specified PID and LockType is possible
 */
bool Resource::canAddOwner(pid_t pid, LockType type)
{
	for (std::map<pid_t, LockType>::iterator iter = currentOwners_.begin(); iter != currentOwners_.end(); iter++)
		if (excludes((*iter).second, type))
			return false;
	
	return true;
}

/**
 * Method used to allow waiting processes access to the resource.
 */
pid_t Resource::update()
{
	for (std::map<pid_t, LockType>::iterator w_iter = waitingProcesses_.begin(); w_iter != waitingProcesses_.end(); w_iter++)
		if (canAddOwner((*w_iter).first, (*w_iter).second))
		{
			pid_t pid = (*w_iter).first;
			currentOwners_.insert(std::pair<int, LockType>(pid, (*w_iter).second));
			waitingProcesses_.erase(pid);

			return pid;
		}

	return -1;
}

/**
 * Method used to remove waiting processes after a timeout.
 */
bool Resource::removeWaiting(pid_t pid)
{
	return (waitingProcesses_.erase(pid) == 1);
}

/**
 * Method used to check whether we can give the resource with specified rights.
 */
bool Resource::excludes(LockType already, LockType candidate)
{
	if (already == EX || candidate == EX)
		return true;
	if (candidate == CR || already == CR)
		return false;
	if (candidate == CW && already < PR)
		return false;
	if (candidate == PR && already == PR)
		return false;

	return true;
}

/**
 * Method used to get currentOwners_ list size.
 */
int Resource::getOwnersSize()
{
	return currentOwners_.size();
}

/**
 * Method used to get waitingProcesses_ list size.
 */
int Resource::getWaitingSize()
{
	return waitingProcesses_.size();
}

std::string Resource::getWaitingLocksStr()
{
	return getUniLocksStr(waitingProcesses_);
}

std::string Resource::getLocksStr()
{
	return getUniLocksStr(currentOwners_);
}

std::string Resource::getUniLocksStr(std::map<pid_t, LockType>& map)
{
	std::string cr("CR: ");
	std::string cw("CW: ");
	std::string pw("PW: ");
	std::string pr("PR: ");
	std::string ex("EX: ");
	char pid_char[10];

	for (std::map<pid_t, LockType>::iterator iter = map.begin(); iter != map.end(); iter++)
	{
		sprintf(pid_char, "%d", (*iter).first);
		if ((*iter).second == CR)
			cr += std::string(pid_char) + std::string(" ");
		else if ((*iter).second == CW)
			cw += std::string(pid_char) + std::string(" ");
		else if ((*iter).second == PW)
			pw += std::string(pid_char) + std::string(" ");
		else if ((*iter).second == PR)
			pr += std::string(pid_char) + std::string(" ");
		else if ((*iter).second == EX)
			ex += std::string(pid_char) + std::string(" ");
	}

	return cr + "\n" + cw + "\n" + pr + "\n" + pw + "\n" + ex + "\n";
}
